﻿namespace Smart.Service
{
    using System;
    using System.IO;
    using System.Security.Cryptography;

    internal class CryptoHelper
    {
        private const int BUFFER_SIZE = 0x20000;
        private const ulong FC_TAG = 18158797384510146255L;
        private static RandomNumberGenerator rand = new RNGCryptoServiceProvider();

        private static bool CheckByteArrays(byte[] b1, byte[] b2)
        {
            if (b1.Length == b2.Length)
            {
                for (int i = 0; i < b1.Length; i++)
                {
                    if (b1[i] != b2[i])
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        private static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
        {
            PasswordDeriveBytes bytes = new PasswordDeriveBytes(password, salt, "SHA256", 0x3e8);
            SymmetricAlgorithm algorithm = Rijndael.Create();
            algorithm.KeySize = 0x100;
            algorithm.Key = bytes.GetBytes(0x20);
            algorithm.Padding = PaddingMode.PKCS7;
            return algorithm;
        }

        public static void DecryptFile(string inFile, string outFile, string password, CryptoProgressCallBack callback)
        {
            using (FileStream stream = File.OpenRead(inFile))
            {
                using (FileStream stream2 = File.OpenWrite(outFile))
                {
                    int length = (int) stream.Length;
                    byte[] buffer = new byte[0x20000];
                    int count = -1;
                    int num3 = 0;
                    int num4 = 0;
                    byte[] buffer2 = new byte[0x10];
                    stream.Read(buffer2, 0, 0x10);
                    byte[] buffer3 = new byte[0x10];
                    stream.Read(buffer3, 0, 0x10);
                    SymmetricAlgorithm algorithm = CreateRijndael(password, buffer3);
                    algorithm.IV = buffer2;
                    num3 = 0x20;
                    long num5 = -1L;
                    HashAlgorithm transform = SHA256.Create();
                    using (CryptoStream stream3 = new CryptoStream(stream, algorithm.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        using (CryptoStream stream4 = new CryptoStream(Stream.Null, transform, CryptoStreamMode.Write))
                        {
                            BinaryReader reader = new BinaryReader(stream3);
                            num5 = reader.ReadInt64();
                            ulong num6 = reader.ReadUInt64();
                            if (18158797384510146255L != num6)
                            {
                                throw new CryptoHelpException("File Corrupted!");
                            }
                            long num7 = num5 / 0x20000L;
                            long num8 = num5 % 0x20000L;
                            for (int i = 0; i < num7; i++)
                            {
                                count = stream3.Read(buffer, 0, buffer.Length);
                                stream2.Write(buffer, 0, count);
                                stream4.Write(buffer, 0, count);
                                num3 += count;
                                num4 += count;
                                callback(0, length, num3);
                            }
                            if (num8 > 0L)
                            {
                                count = stream3.Read(buffer, 0, (int) num8);
                                stream2.Write(buffer, 0, count);
                                stream4.Write(buffer, 0, count);
                                num3 += count;
                                num4 += count;
                                callback(0, length, num3);
                            }
                            stream4.Flush();
                            stream4.Close();
                            stream2.Flush();
                            stream2.Close();
                            byte[] hash = transform.Hash;
                            byte[] buffer5 = new byte[transform.HashSize / 8];
                            count = stream3.Read(buffer5, 0, buffer5.Length);
                            if (!((buffer5.Length == count) && CheckByteArrays(buffer5, hash)))
                            {
                                throw new CryptoHelpException("File Corrupted!");
                            }
                        }
                    }
                    if (num4 != num5)
                    {
                        throw new CryptoHelpException("File Sizes don't match!");
                    }
                }
            }
        }

        public static void EncryptFile(string inFile, string outFile, string password, CryptoProgressCallBack callback)
        {
            using (FileStream stream = File.OpenRead(inFile))
            {
                using (FileStream stream2 = File.OpenWrite(outFile))
                {
                    long length = stream.Length;
                    int max = (int) length;
                    byte[] buffer = new byte[0x20000];
                    int count = -1;
                    int num4 = 0;
                    byte[] buffer2 = GenerateRandomBytes(0x10);
                    byte[] salt = GenerateRandomBytes(0x10);
                    SymmetricAlgorithm algorithm = CreateRijndael(password, salt);
                    algorithm.IV = buffer2;
                    stream2.Write(buffer2, 0, buffer2.Length);
                    stream2.Write(salt, 0, salt.Length);
                    HashAlgorithm transform = SHA256.Create();
                    using (CryptoStream stream3 = new CryptoStream(stream2, algorithm.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        using (CryptoStream stream4 = new CryptoStream(Stream.Null, transform, CryptoStreamMode.Write))
                        {
                            BinaryWriter writer = new BinaryWriter(stream3);
                            writer.Write(length);
                            writer.Write((ulong) 18158797384510146255L);
                            while ((count = stream.Read(buffer, 0, buffer.Length)) != 0)
                            {
                                stream3.Write(buffer, 0, count);
                                stream4.Write(buffer, 0, count);
                                num4 += count;
                                callback(0, max, num4);
                            }
                            stream4.Flush();
                            stream4.Close();
                            byte[] hash = transform.Hash;
                            stream3.Write(hash, 0, hash.Length);
                            stream3.Flush();
                            stream3.Close();
                        }
                    }
                }
            }
        }

        private static byte[] GenerateRandomBytes(int count)
        {
            byte[] data = new byte[count];
            rand.GetBytes(data);
            return data;
        }
    }
}

